package logic.nodes.collision;

import java.util.List;

import settings.CheatConfig;
import logic.nodes.AbsTeamNode;
import logic.nodes.damagable.DamagableNode;
import logic.nodes.lod.blocks.LeafBlock;
import logic.ships.Ship;
import logic.weapons.WeaponFire;
import main.threading.ThreadPoolController;

public class CollisionDetector implements ThreadPoolController {
	
	protected LeafBlock leafblock;
	
	public CollisionDetector(LeafBlock leafBlock) {
		this.leafblock = leafBlock;
	}
	
	@Override
	public void update(Float time) {
		if(!CheatConfig.get().getEnableCollision()) return;
		
		List<CollidableNode> leafNodes = leafblock.getLeafNodes();
		
		for(int i = 0; i < leafNodes.size(); i++) {
			CollidableNode a = leafNodes.get(i);
			if(a == null || !a.allBoundsDone() || !a.isCollidable() || isDestroyed(a)) continue;
			
			checkNode(a);
			
			if(a.canMove()) checkLeafBlock(a);
		}
		
		for(int i = 0; i < leafNodes.size(); i++) {
			CollidableNode node = leafNodes.get(i);
			if(node != null) node.getCurrentCollisions().clear();
		}
	}
	
	protected void checkNode(CollidableNode a) {
		List<CollidableNode> nodes = getCollisionNodes(a);
		for(int i = 0; i < nodes.size(); i++) {
			CollidableNode b = nodes.get(i);
			
			if(b == null || !b.isCollidable() || (!a.canMove() && !b.canMove()) || a == b || ownWeaponFire(a, b) 
			|| isDestroyed(b) || a.getCurrentCollisions().contains(b)) 
				continue;
			
			if(boundsIntersect(a, b)) {
				Collision collision = getCollision(a, b);
				if(collision != null && collision.process()) {
					a.getCurrentCollisions().add(b);
					b.getCurrentCollisions().add(a);
				}
			}
		}
	}
	
	protected Collision getCollision(CollidableNode a, CollidableNode b) {
		boolean sameTeam = isSameTeam(a, b);
		
		boolean aIsDamagable = a instanceof DamagableNode;
		boolean bIsDamagable = b instanceof DamagableNode;
		
		boolean aShieldsUp = false;
		boolean bShieldsUp = false;
		
		if(aIsDamagable) aShieldsUp = shieldsUp(a);
		if(bIsDamagable) bShieldsUp = shieldsUp(b);
		
		if(!sameTeam && (aIsDamagable || bIsDamagable) && (aShieldsUp || bShieldsUp)) 
			return new ShieldCollision(a, b);
		
		return new HullCollision(a, b);
	}
	
	private static boolean isDestroyed(CollidableNode node) {
		return node instanceof DamagableNode && ((DamagableNode)node).isDestroyed();
	}
	
	public static boolean ownWeaponFire(CollidableNode node, CollidableNode child) {
		return (node instanceof Ship && child instanceof WeaponFire && 
			((WeaponFire)child).isWeaponFireOf((Ship)node)) 
		|| (child instanceof Ship && node instanceof WeaponFire && 
			((WeaponFire)node).isWeaponFireOf((Ship)child)) ;
	}
	
	private boolean shieldsUp(CollidableNode node) {
		return ((DamagableNode)node).getCurrentShield() > 0;
	}
	
	private boolean isSameTeam(CollidableNode a, CollidableNode b) {
		if(!CheatConfig.get().getDisableTeamCollision()) return false;
		
		if(a instanceof AbsTeamNode) {
			AbsTeamNode otherTeamNode = (AbsTeamNode)a;
			
			if(b instanceof AbsTeamNode) {
				AbsTeamNode checkerTeamNode = (AbsTeamNode)b;
				return otherTeamNode.getTeam() == checkerTeamNode.getTeam();
			}
		}
		
		return false;
	}
	
	protected List<CollidableNode> getCollisionNodes(CollidableNode node) {
		boolean testWF = node.testAgainstWeaponFire();
		List<CollidableNode> list = (testWF ? leafblock.getLeafNodes() : leafblock.getNoneWeaponFireNodes());
		
		switch(node.getDummyType()) {
			case Client: list = leafblock.getWeaponFireNodes(); break;
			case OnlyCollision: list = leafblock.getNoneWeaponFireNodes(); break;
		}
		
		return list;
	}
	
	protected boolean boundsIntersect(CollidableNode a, CollidableNode b) {
		return a.getBound().intersects(b.getBound());
	}
	
	protected void checkLeafBlock(CollidableNode node) {
		boolean reCheck = false;
		for(int i = 0; i < node.getLeafBlocks().size(); i++) {
			try {
				LeafBlock leafBlock = node.getLeafBlocks().get(i);
				if(leafBlock != null && !leafBlock.intersectsWith(node)) {
					reCheck = true;
					break;
				}
			} catch(IndexOutOfBoundsException e) {}
		}
		
		if(reCheck) {
			node.getLeafBlocks().clear();
			leafblock.getIngameState().addNode(node);
		}
	}
}